/*
**NCS Network Library Routines and Procedures
**This module provide the network API with socket for NCS 
**Because of using only UDP socket,and providing UDP API
**
**Copyright (C) 2015-xxxx andisat <www.andisat.com>
**
**Author: andisat
**Date:2015-12
**
*/
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <sys/un.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <memory.h>
#include "ncs_network.h"
#include "ncs_log.h"

char *optname[]=
{
    "SO_BROADCAST",
    "SO_RCVBUF",
    "SO_SNDBUF",
    "SO_REUSEPORT",
    "SO_REUSEADDR",
    "IP_MULTICAST_LOOP",
    "UNUSING OPTION"
};

const char *get_sockopt_name(int opt)
{
    char *ptr=NULL;
    static char name[SOCKOPT_NAME_MAX];

    memset(name,0,SOCKOPT_NAME_MAX);
    
    switch(opt){
        case SO_BROADCAST:
            ptr=(char *)optname;
            break;
        case SO_RCVBUF:
            ptr=(char *)(optname+1);
            break;
        case SO_SNDBUF:
            ptr=(char *)(optname+2);
            break;
        case SO_REUSEPORT:
            ptr=(char *)(optname+3);
            break;
        case SO_REUSEADDR:
            ptr=(char *)(optname+4);
            break;
        case IP_MULTICAST_LOOP:
            ptr=(char *)(optname+5);
            break;
            
        default:
            ptr=(char *)(optname+6);
            break;
    }

    memcpy(name,ptr,strlen(ptr)+1);

    return name;
}

int ncs_udp_serv_un(const char *path)
{
    int fd,len,value;
    struct sockaddr_un addr;

    if(!path)
        return -1;
    
    unlink (path);
   // old_mask = umask (0777);
    
    if(-1==(fd=socket(AF_UNIX,SOCK_DGRAM,0))){
        ncs_log(ERR,"Open unix socket dgram error ",__func__);
        return fd;
    }

     /*using setsockopt to set  SO_RCVBUF 128K */
    value=128*1024;
    socket_set_option(fd,SOL_SOCKET,SO_RCVBUF,&value,sizeof(value));

    memset(&addr,0,sizeof(struct sockaddr_un));

    addr.sun_family = AF_UNIX;
    strncpy (addr.sun_path, path, strlen (path));
    len = sizeof (addr.sun_family) + strlen (addr.sun_path);

    if(0 > bind(fd,(struct sockaddr *) &addr, len))
    {
        ncs_log(ERR,"%s unix udp connet addr error %s",
            __func__,strerror(errno));
        close(fd);
        return -1;
    }
    
   // umask (old_mask);
    
    ncs_log(INFO,"%s unix server udp (%d) bind %s success ...",
        __func__,fd,path);
    
    return fd;
}

int ncs_udp_client_un(const char *local_path,const char *serv_path)
{
    int fd,len,value;
    struct sockaddr_un serv_addr,local_addr;

    if(!serv_path)
        return -1;
    
    if(-1==(fd=socket(AF_UNIX,SOCK_DGRAM,0))){
        ncs_log(ERR,"Open unix socket dgram error ",__func__);
        return fd;
    }

     /*using setsockopt to set  SO_RCVBUF 128K */
    value=128*1024;
    socket_set_option(fd,SOL_SOCKET,SO_RCVBUF,&value,sizeof(value));
    
    memset(&local_addr,0,sizeof(struct sockaddr_un));
    memset(&serv_addr,0,sizeof(struct sockaddr_un));
    
    unlink(local_path);
    /*bind local addr*/
    if(local_path){
        local_addr.sun_family = AF_UNIX;
        strncpy (local_addr.sun_path, local_path, strlen (local_path));
        len = sizeof (local_addr.sun_family) + strlen (local_addr.sun_path);

        if(0>bind(fd,(struct sockaddr *) &local_addr, len)){
            ncs_log(ERR,"%s unix udp bind addr error %s",
                __func__,strerror(errno));
            close(fd);
            return -1;
        }
    }

    /*connect server addr*/
    serv_addr.sun_family = AF_UNIX;
    strncpy (serv_addr.sun_path, serv_path, strlen (serv_path));

    len = sizeof (serv_addr.sun_family) + strlen (serv_addr.sun_path);

    if(0>connect(fd,(struct sockaddr *) &serv_addr, len)){
        ncs_log(ERR,"%s unix udp connet addr error %s",
            __func__,strerror(errno));
        close(fd);
        return -1;
    }

    ncs_log(INFO,"%s unix udp socket(%d) connet %s success ...",
        __func__,fd,serv_path);
    
    return fd;
}

int ncs_udp_client_open(char *addr,int port)
{
    int fd,value;
    struct sockaddr_in servaddr;
    
     if(-1 == (fd=socket(AF_INET,SOCK_DGRAM,0))){
        ncs_log(ERR,"%s Open udp dgram socket errror ",__func__);
        return fd;
    }
     
    /*using setsockopt to set  SO_RCVBUF 128K */
    value=128*1024;
    socket_set_option(fd,SOL_SOCKET,SO_RCVBUF,&value,sizeof(value));
    memset(&servaddr,0,sizeof(struct sockaddr_in));

    servaddr.sin_family=AF_INET;
    servaddr.sin_port=htons(port);

    if(1!=inet_pton(AF_INET,addr,&servaddr.sin_addr)){
        ncs_log(ERR,"%s input server addr %s is not right ",__func__,addr);
        return -1;
    }

    if(0>connect(fd,(struct sockaddr *) &servaddr, sizeof(struct sockaddr_in))){
        ncs_log(ERR,"%s udp connet addr error %s",
            __func__,strerror(errno));
        close(fd);
        return -1;
    }

    ncs_log(INFO,"%s Open udp client socket(%d) port(%d) success ...",
        __func__,fd,port);
    
    return fd;
    
}

int ncs_udp_serv_open(int port)
{
    int fd,len,value;
    struct sockaddr_in servaddr;
    
    if(-1 == (fd=socket(AF_INET,SOCK_DGRAM,0))){
        ncs_log(ERR,"%s Open udp dgram socket errror ",__func__);
        return fd;
    }

    memset(&servaddr,0,sizeof(struct sockaddr_in));
    
    servaddr.sin_family=AF_INET;
    servaddr.sin_port=htons(port);
    servaddr.sin_addr.s_addr=htonl(INADDR_ANY);

    /*Using setsockopt to turn on SO_BROADCAST SO_REUSEPORT
    SO_REUSEADDR IP_MULTICAST_LOOP options*/
    value=1;
    socket_set_option(fd,SOL_SOCKET,SO_BROADCAST,&value,sizeof(value));
    socket_set_option(fd,SOL_SOCKET,SO_REUSEPORT,&value,sizeof(value));
    socket_set_option(fd,SOL_SOCKET,SO_REUSEADDR,&value,sizeof(value));
    socket_set_option(fd,SOL_SOCKET,IP_MULTICAST_LOOP,&value,sizeof(value));

    /*using setsockopt to get  SO_RCVBUF */
    socket_get_option(fd,SOL_SOCKET,SO_RCVBUF,&value,sizeof(value));
    ncs_log(INFO,"udp socket recvbuf is %d ",value);

    /*using setsockopt to set  SO_RCVBUF 128K */
    value=128*1024;
    socket_set_option(fd,SOL_SOCKET,SO_RCVBUF,&value,sizeof(value));

    socket_get_option(fd,SOL_SOCKET,SO_RCVBUF,&value,sizeof(value));
    ncs_log(INFO,"udp socket recvbuf is %d ",value);
    
    len=sizeof (struct sockaddr_in);
    
    if(-1==bind(fd,(struct sockaddr *)&servaddr,sizeof(struct sockaddr_in)))
    {
        ncs_log(ERR,"%s bind udp socket addr error %s",
                    __func__,strerror(errno));
            close(fd);
            return -1;
    }
    
    ncs_log(INFO,"%s Open udp server socket(%d) port(%d) success ... ",
                __func__,fd,port);
        
    return fd;
}


int socket_set_option(int fd,int level,int optname,
           const void *optval,int optlen)
{
    if(SOL_SOCKET != level 
        && IPPROTO_IP != level 
        && IPPROTO_TCP != level)
    {
        ncs_log(ERR,"setsockopt level(%d) isnot support ...",level);
        return -1;
    }
    
    if(-1==setsockopt(fd,level,optname,optval,optlen))
    {
        ncs_log(ERR,"%s setsockopt %s error ",__func__,
            get_sockopt_name(optname));
        return -1;
    }
    
    return 0;
}

int socket_get_option(int fd,int level,int optname,
            void *optval,int optlen)
{
    if(SOL_SOCKET != level 
        && IPPROTO_IP != level 
        && IPPROTO_TCP != level)
    {
        ncs_log(ERR,"setsockopt level(%d) is not support ...",level);
        return -1;
    }
    
    if(-1==getsockopt(fd,level,optname,optval,&optlen))
    {
        ncs_log(ERR,"%s setsockopt %s error ",__func__,
            get_sockopt_name(optname));
        return -1;
    }
    
    return 0;
}

